#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>

#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>

#include <pthread.h>

#define COMMAND_SERVER_PORT (19080)

static const char *dummy_roots[] = { "/home\t/var", "", "/etc\t/usr" };
//static const char *dummy_roots[] = { "/Developer/Applications\t/System/Library", "", "/usr/include\t/Library/Frameworks" };

static int roots_index = 0;

int read_line(int fd, char *line)
{
    int n = 0;
    int stat;

    while (1) {
        stat = recv(fd, &line[n], 1, MSG_WAITALL);
        if (stat < 0) {     // ERROR
            if (errno == EAGAIN) {
                continue;
            } else {
                n = stat;
                break;
            }
        } else if (stat == 0) { // peer has shut down
            return -1;
        } else {
            line[++n] = '\0';

            if (line[n-1] == '\n') {
                break;
            }
        }
    }

    return n;
}

int send_dummy_resp(int fd, const char *key, const char *value)
{
    char resp[128];
    int len = 0;

    len += sprintf(&resp[len], "rsp\n");
    if (key != NULL && value != NULL) {
        len += sprintf(&resp[len], "%s\t%s\n", key, value);
    }
    len += sprintf(&resp[len], "done\n");

    resp[len] = '\0';

    printf("Response: \n%s", resp);

    while (1) {
        int n = send(fd, resp, len, MSG_WAITALL);
        if (n < 0) {
            perror("send() ERROR"); 
            return -1;
        } else if (n != len) {
            printf("Why send() return value %d != len %d\n", n, len);
            return -1;
        } else { 
            break;
        }
    }

    return 0;
}

void *command_thread(void *data)
{
    int fd = (int)data;
    char *line;
    char *cmd = NULL;

    line = (char *)malloc(2048);
    memset(line, 0, 2048);
 
    while (1) {
        int n = read_line(fd, line);
        if (n < 0) {
            break;
        } 

        printf("Recv line: %s", line);
       
        if (strncmp(line, "done\n", 5) == 0) {
            char resp_key[32];
            char resp_val[128];

            char *p_key = NULL;
            char *p_val = NULL;

            if (strcmp(cmd, "query_stat\n") == 0) {
                int stat = random() % 3;

                sprintf(resp_key, "stat");
                sprintf(resp_val, "%d", stat);
                p_key = resp_key;
                p_val = resp_val;
            } else if (strcmp(cmd, "get_ns_roots\n") == 0) {
                sprintf(resp_key, "path");
                sprintf(resp_val, "%s", dummy_roots[roots_index]);
                roots_index = (roots_index + 1) % (sizeof(dummy_roots)/sizeof(dummy_roots[0]));
                p_key = resp_key;
                p_val = resp_val;
            }

            if (send_dummy_resp(fd, p_key, p_val) < 0) {
                // Send dummy response failed
                printf("send_dummy_resp() failed\n");
                break;
            } else {
                printf("send_dummy_resp done\n");
            }

            printf("\n");

            // reset command
            free(cmd);
            cmd = NULL;
        } else if (cmd == NULL) {
            // first line: command name
            cmd = strdup(line);
        } else {
            // parameter line; format: key\tvalue\n
            char *tab = strchr(line, '\t');
            if (tab == NULL) {
                printf(" ---- bad line\n");
            } else {
                // TODO parse key/values pair
            }
        }
    }

    printf("thread will exit ...\n");
    close(fd);
    return NULL;
}

int main(int argc, char *argv[])
{
    int sock = -1;
    struct sockaddr_in addr;
    struct timeval tv = { 3, 0 };   // Timeout: 3 seconds
    int enable_reuse = 1;
    struct linger linger;
    int ret;

    /* Ignore the broken PIPE signal */
    signal(SIGPIPE, SIG_IGN);

    /* intialize address structure */
    addr.sin_family = AF_INET;                                              
    addr.sin_port = htons(COMMAND_SERVER_PORT);
    inet_aton("127.0.0.1", &addr.sin_addr);

    sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sock < 0) {
        goto TRY_CONNECT_FAIL;
    }

    /* set timeout on socket, to protect against bad servers */
    ret = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval));
    if (ret < 0) {
        printf("setsockopt() RCVTIMEO failed\n");
        goto TRY_CONNECT_FAIL;
    }

    ret = setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(struct timeval));
    if (ret < 0) {
        printf("setsockopt() SNDTIMEO failed\n");
        goto TRY_CONNECT_FAIL;
    }

    ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable_reuse, sizeof(enable_reuse));
    if (ret != 0) {
        printf("setsockopt() SO_REUSEADDR failed\n");
        goto TRY_CONNECT_FAIL;
    }   
    
    linger.l_onoff  = 1;
    linger.l_linger = 1;
    ret = setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
    if (ret != 0) {
        printf("setsockopt() SO_LINGER failed");
        goto TRY_CONNECT_FAIL;
    }

    ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
    if (ret < 0) {
        printf("bind() failed\n");
        goto TRY_CONNECT_FAIL;
    }

    ret = listen(sock, 32);
    if (ret < 0) {
        printf("listen() failed\n");
        goto TRY_CONNECT_FAIL;
    } 

    printf("Dummy server is listening at 127.0.0.1:%d ...\n", COMMAND_SERVER_PORT);

    while (1) {
        pthread_t   pid;
        int connfd = accept(sock, NULL, NULL);
        if (connfd < 0) {
            //printf("accept() failed or timeout; continue ...\n");
            continue;
        }

        if (pthread_create(&pid, NULL, command_thread, (void *)connfd) != 0) {
            printf("pthread_create() failed; close connection.\n");
            close(connfd);
        }
    }

    return 0;

TRY_CONNECT_FAIL:
    perror("ERROR");
    if (sock >= 0) {
        close(sock);
    }
    return -1;
}

